require 'cgi'
require 'json'

$KCODE = "U"

require 'dm-core'
DataMapper.setup(:default, "appengine://auto")

require 'fw/modules/util/loader'


class App

  @@instance ||= nil

	def self.call env
    begin
      (@@instance ||= instantiate).wrap_call(
        RequestEvent.from_env(env)
      )
    rescue Exception => e
      show_exception env, e
    end
	end

  def self.dev_mode?
    ENV_JAVA['com.google.appengine.runtime.environment'] ==
        'Development'
  end

  def self.instantiate
    if dev_mode?
      DevelopmentMode.new
    else
      ProductionMode.new
    end
  end

  def initialize    
    @@root = File.dirname(__FILE__)
    load_files
#    DmLogger.new().subscribe(Log)
    puts "initialing the app"
    Event.fire('app:init')
  end

  def load_files
    Kernel.load(__FILE__)
    files = []
    [ '/modules/util/*.rb',
      '/modules/*/*.rb',
      '/../app/*/*.rb',
    ].each  { |dir|
      files.push(*Dir[@@root+dir].to_a)
    }
    Loader.load_files files
  end


  def call e
    process_url(e) || no_response
  end

  def process_url e
    url = ((e.env["PATH_INFO"] || '/').split('/')[1] || '')
    try_url(e, url) || try_url(e, '')
  end

  def try_url e, url
    e.name = 'url:'+url
    responses = e.fire()

    if response = responses.first
      if r = response.data['html'] || response.data['response']
        if r.is_a? String
          Rack::Response.new(r)
        elsif r.is_a? Rack::Response
          r
        else
          raise 'Dunno what to do with response: '+r.inspect
        end
      end
    end
  end

  def self.show_exception env, e
    show500(
        Rack::ShowExceptions.new(self).pretty(env, e)[0]
      )
  end

  def self.show500 html
    Rack::Response.new(
      html || '<center>We are sorry, there was an error</center>',
      500
    )
  end

  def self.show404 html
    Rack::Response.new(
      html || '<h1>No response.</h1>',
      404
    )
  end

end



class ProductionMode < App

  def no_response
    show404("No response.")
  end

  def wrap_call e
    self.class.once.each { |b| b.call }
    begin
      call(e).finish
    rescue Exception => exception
      #Log.exception(exception, e.env)
      #self.class.show500
      self.class.show_exception(e.env, exception)
    end
  end
end


class DevelopmentMode < App

  def no_response
    raise("No response.")
  end

  def wrap_call e
    #log_request env
    load_files
    self.class.once.each { |b| b.call }
    begin
      log_request e
      call(e).finish
    rescue Exception => exception
      #puts exception.message + "\n\t" + exception.backtrace.join("\n\t")
      self.class.show_exception(e.env, exception)
    end
  end

  def log_request e
    line = "\n["+e.env['REQUEST_METHOD'] + "] - " + e.url
    if e.env['REQUEST_METHOD'] == 'POST'
      justify = e.params.map { |k, v| k.length }.max
      e.params.each { |k,v|
        line << "\n    "+k.ljust(justify) + ' => ' + v.inspect
      }
    end
    Kernel.puts line
  end
end


